package com.wish233.spi;

import cn.hutool.core.io.resource.ResourceUtil;
import com.wish233.serializer.Serializer;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author WHH
 * SPI加载类
 */
@Slf4j
public class SpiLoader {

    //map储存已加载的类
    public static final Map<String, Map<String, Class<?>>> LOADED_CLASS_MAP = new ConcurrentHashMap<>();
    //缓存对象实例
    public static final Map<String, Object> CACHE = new ConcurrentHashMap<>();
    //要加载系统的类，优先加载用户的配置
    public static final String RPC_SYSTEM_SPI_DIR = "META-INF/rpc/system/";
    public static final String RPC_USER_SPI_DIR = "META-INF/rpc/user/";
    public static final List<String> SCAN_DIRS = Arrays.asList(RPC_SYSTEM_SPI_DIR, RPC_USER_SPI_DIR);
    public static final List<Class<?>> LOADED_CLASS_LIST = Collections.singletonList(Serializer.class);

    public static void loadAllSpi() {
        log.info("加载所有SPI");
        for (Class<?> clazz : LOADED_CLASS_LIST) {
            load(clazz);
        }
    }

    /**
     * 加载某个SPI
     *
     * @param loadClass 要加载的类型
     * @return
     */
    public static Map<String, Class<?>> load(Class<?> loadClass) {
        log.info("加载的类型为: {}", loadClass.getName());
        Map<String, Class<?>> keyClassMap = new HashMap<>();
        //扫描路径
        for (String dir : SCAN_DIRS) {
            List<URL> resources = ResourceUtil.getResources(dir + loadClass.getName());
            //读取URL
            for (URL url : resources) {
                try {
                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(url.openStream()));
                    String line;
                    while ((line = bufferedReader.readLine()) != null) {
                        log.info(line);
                        String[] split = line.split("=");
                        if (split.length > 1) {
                            String name = split[0];
                            String className = split[1];
                            keyClassMap.put(name, Class.forName(className));
                        }
                    }
                } catch (Exception e) {
                    log.info("加载SPI失败 原因 {}", e.getMessage());
                }
            }
        }
        LOADED_CLASS_MAP.put(loadClass.getName(), keyClassMap);
        return keyClassMap;
    }

    public static <T> T getSpiInstance(Class<?> tClass, String key) {
        String tClassName = tClass.getName();
        Map<String, Class<?>> keyClassMap = LOADED_CLASS_MAP.get(tClassName);
        if (keyClassMap == null) {
            throw new RuntimeException(String.format("Spi 还未加载 %s 类型", tClassName));
        }
        if (!keyClassMap.containsKey(key)) {
            throw new RuntimeException(String.format("Spi 不支持%s", key));
        }
        //获取要加载的类
        Class<?> implClass = keyClassMap.get(key);
        String implClassName = implClass.getName();
        //从缓存中找
        if (CACHE.containsKey(implClassName)) {
            return (T) CACHE.get(implClassName);
        }
        //缓存不存在，实例化一个
        Object newInstance;
        try {
            newInstance = implClass.newInstance();
            CACHE.put(implClassName, newInstance);
        } catch (InstantiationException | IllegalAccessException e) {
            log.error("实例化失败 {}", e.getMessage());
            throw new RuntimeException(e);
        }
        return (T) newInstance;
    }

}
